Skip to content

Exercises

useMousePosition

In another earlier exercise, we tracked the user's mouse position to display it in a MouseCoords component.

Let's pull this logic into a generic, reusable hook called useMousePosition.

Code Playground

import React from 'react';

import useMousePosition from './hooks/use-mouse-position.js';

// TODO: Pull the mouse-position logic into
// the use-mouse-position.js file!

function App() {
const [mousePosition, setMousePosition] = React.useState({
x: 0,
y: 0,
});
React.useEffect(() => {
function handleMouseMove(event) {
setMousePosition({
x: event.clientX,
y: event.clientY,
});
}

window.addEventListener('mousemove', handleMouseMove);

return () => {
window.removeEventListener('mousemove', handleMouseMove);
};
}, []);

return (

Solution:

(If you're curious how the "Magnetic lines" effect works from my blog, I did a walkthrough on some of the basic concepts on the Frontend Horse Holiday Snowtacular in 2021. It uses this exact custom hook!)

useIsOnscreen

In the Toasty! exercise, we saw how to use IntersectionObserver to trigger a state change when an element enters/exits the viewport.

Let's generalize this solution so that we can easily check if a given element is in the viewport or not.

In the exercise below, you'll see a large red square that can be scrolled into view. Your job is to fill in the useIsOnscreen hook so that we track this element.

Acceptance Criteria:

  • When the red square is in the viewport, we should see the word “YES” in the top right corner
  • When the red square is not in the viewport, we should see the word “NO” instead.
  • We shouldn't use document.querySelector.
  • There should be no lint warnings

Code Playground

import React from 'react';

import useIsOnscreen from './hooks/use-is-onscreen.js';

function App() {
const isOnscreen = useIsOnscreen();
return (
<>
<header>
Red box visible: {isOnscreen ? 'YES' : 'NO'}
</header>
<div className="wrapper">
<div className="red box" />
</div>
</>
);
}

export default App;

Solution:

The video mentions a blog post I wrote, Animated Sparkles in React. It's not particularly relevant, but I thought I'd link it in case you were curious!

useToggle

In the Digital Clock exercise, we could click a button to toggle the clock off and on:

This happens because of some conditional rendering inside /App.js. The code looks something like this:

const [
showClock,
setShowClock
] = React.useState(true);
return (
<>
<button onClick={() => setShowClock(!showClock)}>
Clock {showClock ? 'ON' : 'OFF'}
</button>
{showClock && <Clock />}
</>
);

This works fine, but one of the coolest things about React hooks is that we can create custom utility hooks that work even better.

For example, what if we created a new useToggle hook? This hook should behave exactly like useState, but instead of setting the state to a specified value, it should toggle back and forth between true and false:

function App() {
const [
showClock,
toggleClock,
] = useToggle(true);
return (
<>
<button onClick={toggleClock}>
Clock {showClock ? 'ON' : 'OFF'}
</button>
{showClock && <Clock />}
</>
);
}

For the most part, we use this useToggle hook just like we'd use the built-in useState hook: we give it an initial value (true, in this case), and it provides the current value and a function to change the value.

The difference is that the useState hook provides a state-setter function, which accepts the next value for the state variable. Our useToggle hook provides a toggle function (eg. toggleClock). It ignores any arguments passed to it. Calling the toggle function will flip the value from true to false, or from false to true.

Your job is to create this useToggle hook.

Acceptance Criteria:

  • The useToggle hook should use useState internally, to create and manage a state variable.
  • We should be able to specify an initial value for the state variable.
  • It should return an array containing two items:
    • The state variable
    • A function that flips the state variable between true and false.
  • Clicking the button should toggle the clock off and on.

Code Playground

import React from 'react';

import useToggle from './hooks/use-toggle'
import Clock from './Clock'

function App() {
// TODO: Replace this with “useToggle”!
const [
showClock,
setShowClock
] = React.useState(true);
return (
<>
<button
className="clock-toggle"
onClick={() => setShowClock(!showClock)}
>
{showClock ? 'Clock ON' : 'Clock OFF'}
</button>
{showClock && <Clock />}
</>
);
}

export default App;
  1. tick
  2. tick
  3. tick

Solution:

Correction: In this solution, we restrict initialValue to boolean values, but to truly work as a drop-in replacement for useState, we should also support initializer functions. This tweak has been made to the solution code here: